#include "rsuconnect.h"
#include <QTimer>
#include <QtEndian>

RsuConnect::RsuConnect(QObject *parent) : QObject(parent)
{
    connect(&m_tcpSocket, &QTcpSocket::connected,
            this, &RsuConnect::socketConnected);
    connect(&m_tcpSocket, &QTcpSocket::disconnected,
            this, &RsuConnect::socketDisconnected);
    connect(&m_tcpSocket, &QTcpSocket::readyRead,
            this, &RsuConnect::socketReadyRead);
    connect(&m_tcpSocket, &QAbstractSocket::stateChanged,
             this, &RsuConnect::stateChanged);
    connect(&m_tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
            this, SLOT(socketError(QAbstractSocket::SocketError)));
}

RsuConnect::~RsuConnect()
{

}

void RsuConnect::tryReconnect()
{
   if (QAbstractSocket::UnconnectedState==m_tcpSocket.state())
   {
       m_tcpSocket.connectToHost(m_sHostAddr, m_nHostPort);
   }
}

// 帧头
const static char *STX="\xFF\xFF";
// 帧尾
const static char *ETX="\xFF";
// 除去帧头、帧尾的最小帧长度：RSCTL(1) TYPE(1) BCC(1)
const static int MIN_FRAME_SIZE=3;
// 带帧头、帧尾的最小帧长度
const static int MIN_FRAME_SIZE_WITH_FRAME_HEAD_AND_END=6;

void RsuConnect::socketConnected()
{
    qDebug()<<"rsu 连接成功";
}

void RsuConnect::socketDisconnected()
{
    qDebug()<<"rsu 连接断开";
}

void RsuConnect::socketBytesWritten(qint64 bytes)
{
    qDebug()<<tr("<%1:%2>: [%3] bytes written.")
              .arg(m_sHostAddr)
              .arg(m_nHostPort)
              .arg(bytes);
}

void RsuConnect::socketReadyRead()
{
    QByteArray dataRead = m_tcpSocket.readAll();
    if(dataRead.length() == 0)
    {
        qDebug()<<tr("<%1:%2>: 0 byte read, disconnect.")
                       .arg(m_sHostAddr)
                       .arg(m_nHostPort);
        m_tcpSocket.disconnectFromHost();
        return;
    }
    processDataRead(dataRead,NULL,0);
}

bool RsuConnect::processDataRead(const QByteArray &dataRead, QByteArray *pExpectedFrame, quint8 nExpectedFrameType)
{
    m_dataRead.append(dataRead);

    bool bGotExpectedFrame = false;

    while (m_dataRead.length()>MIN_FRAME_SIZE_WITH_FRAME_HEAD_AND_END)
    {
        int start = m_dataRead.indexOf(STX, 0);
        if (start<0)
        {
            // 没有包头，丢弃所有数据
            qDebug()<<tr("<%1:%2>: no frame head, discard %3 bytes.")
                            .arg(m_sHostAddr)
                            .arg(m_nHostPort)
                            .arg(m_dataRead.length());
            m_dataRead.clear();
            return false;
        }
        int end = m_dataRead.indexOf(ETX, strlen(STX));
        if (end<0)
        {
            // 没有包尾，等待再次接收数据
            return false;
        }
        // 抽取去除包头、包尾的帧数据
        QByteArray frame = m_dataRead.mid(start+strlen(STX), end-start-strlen(STX));
        // 从读入缓存中去除本帧数据（连同包头、包尾）
        m_dataRead.remove(0, end+strlen(ETX));
        if (processRawFrame(frame, pExpectedFrame, nExpectedFrameType))
        {
            bGotExpectedFrame=true;
        }
    }
    return bGotExpectedFrame;

}

bool RsuConnect::processRawFrame(QByteArray &frame, QByteArray *pExpectedFrame, quint8 nExpectedFrameType)
{
    // 长度检查
    if (frame.length()<MIN_FRAME_SIZE)
    {
        // 长度不足，丢弃帧数据
        qDebug()<<tr("<%1:%2>: too short, discard frame[%3]")
                        .arg(m_sHostAddr)
                        .arg(m_nHostPort)
                        .arg(frame.toHex().data());
        return false;
    }

    // BCC校验
    quint8 bcc = getXOR(frame);
    if (0!=bcc)
    {
        // BCC校验失败，丢弃帧数据
        qDebug()<<tr("<%1:%2>: bcc error, discard frame[%3]")
                        .arg(m_sHostAddr)
                        .arg(m_nHostPort)
                        .arg(frame.toHex().data());
        return false;
    }
    frame.replace("\xFE\x01", 2, "\xFF", 1);
    frame.replace("\xFE\x00", 2, "\xFE", 1);

    qDebug()<<tr("<%1:%2>: frame[%3]")
                    .arg(m_sHostAddr)
                    .arg(m_nHostPort)
                    .arg(frame.toHex().data());

    quint8 nFrameIndex=frame[0];
    quint8 nFrameType=frame[1];
    // 去除帧序号和帧类型
    frame.remove(0, 2);
    // 去除BCC
    frame.remove(frame.length()-1, 1);
    if (NULL!=pExpectedFrame)
    {
        bool bRlt=(nFrameType==nExpectedFrameType);
        if (bRlt)
        {
            pExpectedFrame->clear();
            pExpectedFrame->append(frame);
        }
        return bRlt;
    }
    else
    {
        processFrame(nFrameIndex, nFrameType, frame);
        return false;
    }
}

// 异或值
quint8 RsuConnect::getXOR(const QByteArray &data)
{
    quint8 n=0x00;
    for(int it=0; it<data.length(); ++it)
    {
        n ^= data[it];
    }
    return n;
}

void RsuConnect::processFrame(quint8 nFrameIndex, quint8 nFrameType, QByteArray &frame)
{
    emit newFrameArrived(nFrameIndex, nFrameType, frame);
}

void RsuConnect::socketStateChanged(QAbstractSocket::SocketState socketState)
{
    qDebug()<<tr("<%1:%2>: Socket状态变化[%3]")
                    .arg(m_sHostAddr)
                    .arg(m_nHostPort)
                    .arg(socketState);
}

void RsuConnect::socketError(QAbstractSocket::SocketError socketError)
{
    qDebug()<<tr("<%1:%2>: Socket错误[%3][%4]")
                    .arg(m_sHostAddr)
                    .arg(m_nHostPort)
                    .arg(socketError)
                    .arg(this->m_tcpSocket.errorString());
    QTimer::singleShot(10000, this, &RsuConnect::tryReconnect);

}

void RsuConnect::wrapFrame(QByteArray &data)
{
    data.replace("\xFE", 1, "\xFE\x00", 2);
    data.replace("\xFF", 1, "\xFE\x01", 2);
    quint8 rsctl = getRSCTL();
    data.prepend(rsctl);
    quint8 bcc = getXOR(data);
    data.append(bcc);
    data.prepend(STX);
    data.append(ETX);
}

int RsuConnect::wrapAndSendFrame(QByteArray &data)
{
    if (char(0xF1)!=data[0])
    {
        qDebug()<<tr("<%1:%2>: command[%3]")
                        .arg(m_sHostAddr)
                        .arg(m_nHostPort)
                        .arg(data.toHex().data());
    }

    wrapFrame(data);

    qint64 nWrittenDataLen=m_tcpSocket.write(data);
    if (nWrittenDataLen!=data.length())
    {
        // Note: 20151221使用成谷天线控制盒测试发现：
        //  如果天线电源被关闭，write仍返回成功，直到约3分钟后才有
        // socketStateChanged等事件，所以这个判断条件基本不会被走到！
        qDebug()<<"m_tcpSocket.write failure";
        return ERR_RSU_OTHER;
    }
    return ERR_SUCCESS;
}

#define PRE_CHECK_FOR_RQ \
    if (QAbstractSocket::ConnectedState!=this->m_tcpSocket.state()) \
    { \
        return ERR_RSU_NOT_OPENED; \
    }

#define PRE_CHECK_FOR_RS PRE_CHECK_FOR_RQ


// 执行 用来初始化RSU
int RsuConnect::initRsuRq(char *Time, int BSTInterval, int TxPower, int PLLChannelID)
{
    PRE_CHECK_FOR_RQ;

    QByteArray data;
    data.append(0xF0);
    data.append(Time, 4);
    data.append(quint8(BSTInterval));
    data.append(quint8(TxPower));
    data.append(quint8(PLLChannelID));
    return wrapAndSendFrame(data);
}

// 执行 获取rsu信息
int RsuConnect::rsuInfoRq()
{
    PRE_CHECK_FOR_RQ;

    QByteArray data;
    data.append(0xF7);
    return wrapAndSendFrame(data);
}

// 给天线发送bst帧
int RsuConnect::initPrimRq(char *BeaconID, char *Time, int Profile, int MandApplicationlist, char *MandApplication, int Profilelist)
{
    PRE_CHECK_FOR_RQ;
    QByteArray data;
    data.append(0xF1);

//    if (RSU_TYPE_CHENGGU==m_nRsuType)
//    {
        // LID
        data.append("\xFF\xFF\xFF\xFF", 4);
        // 含义未知
        data.append("\x50\x03", 2);
//    }
    // PDU号码，不得设定到 0000 或 0001
    quint8 pdu=0x02;
    // 分段字头，在pdu=0x02时，应该为0x91
    quint8 head = 0x81 | (pdu<<3);
    data.append(head);
    // INITIALIZATION.request，按GBT-20851.3-2007附录B
    data.append(0xC0);
    data.append(BeaconID, 4);
    data.append(Time, 4);
    data.append(quint8(Profile));
    data.append(quint8(MandApplicationlist));
    // 不知道为何要去掉本条命令的长度（只有一条命令）
    data.append(MandApplication+1, getCmdGroupLen(MandApplicationlist, MandApplication)-1);
    data.append(quint8(Profilelist));
    return wrapAndSendFrame(data);
}

/*!
 * \brief 《GBT_28421-2012_电子收费_基于专用短程通信的电子收费交易.pdf》 7.10.2 Event-Report-Request
 */
// 释放OBU

int RsuConnect::reportEventRq(int Mode, int DID, int EventType)
{
    PRE_CHECK_FOR_RQ;

    Q_UNUSED(Mode);

    QByteArray data;
    data.append(0xF3);
    // 含义未知
    data.append("\x02\xCA\x21\x2F\x40\x03\x91\x60", 8);
    data.append(quint8(DID));
    data.append(quint8(EventType));
    return wrapAndSendFrame(data);
}

// wrapframe 帧时用到此函数,每次发送指令都调用wrapframe函数
quint8 RsuConnect::getRSCTL()
{
   if (m_frameIndex>7)
   {
      m_frameIndex=0;
   }
   quint8 n=0x80+m_frameIndex;
   m_frameIndex++;
   return n;
}

